package testClasses.misc;

import galaxies.GalaxySizeCalculator;
import gameAppLayer.GameSettings;

import java.awt.Polygon;
import java.awt.geom.Area;
import java.awt.geom.Rectangle2D;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import java.util.Random;
import java.util.logging.Level;
import java.util.logging.Logger;

import com.jme3.material.Material;
import com.jme3.math.ColorRGBA;
import com.jme3.scene.Geometry;
import com.jme3.scene.Mesh.Mode;

import controller.AbstractController;

import testClasses.ATest;
import testClasses.TestingSystem;
import utilities.CoordConvert;
import utilities.CustomMesh;
import utilities.Key;

public class GalaxyArrangeTest extends ATest {

	private static final Logger logger = Logger.getLogger(GalaxyArrangeTest.class.getName());
	
	public GalaxyArrangeTest(TestingSystem system) {
		super(system);
		name = "ArrangeGalaxyTest";
		logger.log(Level.INFO, "Created");
	}

	@Override
	public boolean execute() {
		logger.log(Level.INFO, "Execute()");
		
		HashMap<Key, AbstractController> controllerMap = new HashMap<Key, AbstractController>();
		eventManager.addControlMapReference(controllerMap);
		
		TestGameSettingsDefaults defaults = new TestGameSettingsDefaults();
		GameSettings settings = defaults.settings;
		
		GalaxySizeCalculator galSizeCalc = new GalaxySizeCalculator();
		HashMap<String, ArrayList<ArrayList<Integer>>> galBounds = new HashMap<String, ArrayList<ArrayList<Integer>>>();//galSizeCalc.calcGalaxyDims(settings);
		
		//Galaxy 1, rectangle
		ArrayList<ArrayList<Integer>> gal1Bounds = new ArrayList<ArrayList<Integer>>();
		for (int i = 0; i < 4; i++) {
			gal1Bounds.add(new ArrayList<Integer>());			
		}
		gal1Bounds.get(0).add(10); //upper right
		gal1Bounds.get(0).add(10);
		
		gal1Bounds.get(1).add(-10);//upper left
		gal1Bounds.get(1).add(10);
		
		gal1Bounds.get(2).add(-10);//lower left
		gal1Bounds.get(2).add(-10);
		
		gal1Bounds.get(3).add(10); //lower right
		gal1Bounds.get(3).add(-10);
		
		galBounds.put("rect1", gal1Bounds);
		
		
		ArrayList<ArrayList<Integer>> gal6Bounds = new ArrayList<ArrayList<Integer>>();
		for (int i = 0; i < 4; i++) {
			gal6Bounds.add(new ArrayList<Integer>());			
		}
		gal6Bounds.get(0).add(10); //upper right
		gal6Bounds.get(0).add(10);
		
		gal6Bounds.get(1).add(-10);//upper left
		gal6Bounds.get(1).add(10);
		
		gal6Bounds.get(2).add(-10);//lower left
		gal6Bounds.get(2).add(-10);
		
		gal6Bounds.get(3).add(10); //lower right
		gal6Bounds.get(3).add(-10);
		
		galBounds.put("rect2", gal6Bounds);
		
		
		
		//Galaxy 2, triangle
		ArrayList<ArrayList<Integer>> gal2Bounds = new ArrayList<ArrayList<Integer>>();
		for (int i = 0; i < 3; i++) {
			gal2Bounds.add(new ArrayList<Integer>());			
		}
		
		gal2Bounds.get(0).add(50); //bottom right
		gal2Bounds.get(0).add(0);
		
		gal2Bounds.get(1).add(0);
		gal2Bounds.get(1).add(80); //top point
		
		gal2Bounds.get(2).add(-35);
		gal2Bounds.get(2).add(-20); //bottom left
		
		galBounds.put("tri", gal2Bounds);
		
//		Polygon poly1 = new Polygon();
//		poly1.addPoint(50, 0);
//		poly1.addPoint(0, 80);
//		poly1.addPoint(-35, -20);
//		
//		System.out.println(poly1.contains(-2, 60));
		
		
		
//		//Galaxy 3, pentagon
//		ArrayList<ArrayList<Integer>> gal3Bounds = new ArrayList<ArrayList<Integer>>();
//		for (int i = 0; i < 5; i++) {
//			gal3Bounds.add(new ArrayList<Integer>());			
//		}
//		
//		gal3Bounds.get(0).add(20);
//		gal3Bounds.get(0).add(10);
//		
//		gal3Bounds.get(1).add(0);
//		gal3Bounds.get(1).add(20);
//		
//		gal3Bounds.get(2).add(-20);
//		gal3Bounds.get(2).add(10);
//		
//		gal3Bounds.get(3).add(-10);
//		gal3Bounds.get(3).add(-20);
//		
//		gal3Bounds.get(4).add(10);
//		gal3Bounds.get(4).add(-20);
//		
//		galBounds.put("pent", gal3Bounds);
		
//		//Galaxy 4, hexagon missing a sector
//		ArrayList<ArrayList<Integer>> gal4Bounds = new ArrayList<ArrayList<Integer>>();
//		for (int i = 0; i < 8; i++) {
//			gal4Bounds.add(new ArrayList<Integer>());			
//		}
//		
//		gal4Bounds.get(0).add(20);
//		gal4Bounds.get(0).add(20);
//		
//		gal4Bounds.get(1).add(0);
//		gal4Bounds.get(1).add(40);
//		
//		gal4Bounds.get(2).add(-20);
//		gal4Bounds.get(2).add(20);
//		
//		gal4Bounds.get(3).add(0);
//		gal4Bounds.get(3).add(0);
//		
//		gal4Bounds.get(4).add(-20);
//		gal4Bounds.get(4).add(-20);
//		
//		gal4Bounds.get(5).add(0);
//		gal4Bounds.get(5).add(-40);
//		
//		gal4Bounds.get(6).add(20);
//		gal4Bounds.get(6).add(-20);
//		
//		gal4Bounds.get(7).add(20);
//		gal4Bounds.get(7).add(20);
//		
//		galBounds.put("hex1", gal4Bounds);
		
		//Galaxy 4, hexagon missing a sector
		ArrayList<ArrayList<Integer>> gal5Bounds = new ArrayList<ArrayList<Integer>>();
		for (int i = 0; i < 7; i++) {
			gal5Bounds.add(new ArrayList<Integer>());			
		}
		
		gal5Bounds.get(0).add(20);
		gal5Bounds.get(0).add(20);
		
		gal5Bounds.get(1).add(0);
		gal5Bounds.get(1).add(40);
		
		gal5Bounds.get(2).add(-20);
		gal5Bounds.get(2).add(20);
		
		gal5Bounds.get(3).add(0);
		gal5Bounds.get(3).add(0);
		
		gal5Bounds.get(4).add(-20);
		gal5Bounds.get(4).add(-20);
		
		gal5Bounds.get(5).add(0);
		gal5Bounds.get(5).add(-40);
		
		gal5Bounds.get(6).add(20);
		gal5Bounds.get(6).add(-20);
		
//		gal5Bounds.get(7).add(20);
//		gal5Bounds.get(7).add(20);
		
		galBounds.put("hex2", gal5Bounds);
		
		
		
//		//Galaxy 5, 5-sided star
//		ArrayList<ArrayList<Integer>> gal5Bounds = new ArrayList<ArrayList<Integer>>();
//		for (int i = 0; i < 10; i++) {
//			gal5Bounds.add(new ArrayList<Integer>());			
//		}
		
		logger.log(Level.INFO, "Created shapeBounds HashMap");
		
		
		
		HashMap<String, ArrayList<Integer>> offsets = new HashMap<String, ArrayList<Integer>>();
		
		//create polygons out of dimensions
		ArrayList<Polygon> polys = new ArrayList<Polygon>();
		ArrayList<String> galRefs = new ArrayList<String>();
		String galRef;
		for (Map.Entry<String, ArrayList<ArrayList<Integer>>> entry : galBounds.entrySet()) {
			galRef = entry.getKey();
			
			Polygon poly = new Polygon();
			for (ArrayList<Integer> point: entry.getValue()) {
				poly.addPoint(point.get(0), point.get(1));
			}
			
			polys.add(poly);
			galRefs.add(galRef);
		}
		
		logger.log(Level.INFO, "Created polys");
		
//		ArrayList<ColorRGBA> colorList = new ArrayList<ColorRGBA>();
//		colorList.add(ColorRGBA.Blue);
//		colorList.add(ColorRGBA.Brown);
//		colorList.add(ColorRGBA.Cyan);
//		colorList.add(ColorRGBA.Magenta);
//		colorList.add(ColorRGBA.Orange);
//		colorList.add(ColorRGBA.Pink);
//		colorList.add(ColorRGBA.Red);
//		colorList.add(ColorRGBA.White);
//		colorList.add(ColorRGBA.Yellow);
//		colorList.add(ColorRGBA.Gray);		
//		
//		//ArrayList<Double> loc;
//		float x1;
//		float y1;
//		CustomMesh mesh1;
//		Geometry geo1;
//		Material mat1;
//		int j = 0;
//		for (Polygon poly: polys) {
//			mesh1 = new CustomMesh(poly.xpoints.length);
//			mesh1.setMode(Mode.LineLoop);
//			
//			if (j > colorList.size()-1) {
//				j = 0;
//			}
//			mesh1.setColor(colorList.get(j));						
//			mesh1.setPointSize(5);		
//			
//			for (int i = 0; i < poly.npoints; i++) { //ArrayList<Integer> location : locations) {
//				
//				x1 = poly.xpoints[i];
//				y1 = poly.ypoints[i];
//				
//				mesh1.addVertex(x1, y1, 0);			
//			}
//			mesh1.finish();
//			mesh1.setStatic();
//			
//			geo1 = new Geometry("points", mesh1);
//			mat1 = new Material(assets, "Common/MatDefs/Misc/Unshaded.j3md");
//			mat1.setBoolean("VertexColor", true);
//			//mat.setColor("Color", ColorRGBA.Green);
//			geo1.setMaterial(mat1);
//			node.attachChild(geo1);
//			j++;
//		}
		
		
		
		
		
		
		
		/*
		 * Algorithm:
		 * 1) Place the first polygon near (0,0) point.
		 * 2) Choose next polygon.
		 * 3) Choose a random already-placed polygon and a random direction.
		 * 4) Step out from the random poly in the selected direction, checking for collisions.
		 * 5) When no collision, place the current poly.  Convert displacement to hexCoords, record it and record galRef
		 * 6) Repeat from (2)
		 */
		Random rand = new Random();
		double x,y;
		int sign;
		
		//Choose starting displacement somewhere within +/-10 of origin
		if (rand.nextDouble() >=0.5) {sign = 1;}
		else {sign = -1;}		
		x = sign*rand.nextInt(10);
		if (rand.nextDouble() >=0.5) {sign = 1;}
		else {sign = -1;}
		y = sign*rand.nextInt(10);
		
		polys.get(0).translate((int) x, (int) y); //can only translate Polygons with ints...
		galRef = galRefs.get(0);
		
		logger.log(Level.INFO, "Translated " + galRef + " by (x,y)= " + x + " " + y);
		
		ArrayList<Double> translation = new ArrayList<Double>();
		translation.add(Math.floor(x)); //Math.floor effectively makes x, y into their int values but as doubles
		translation.add(Math.floor(y));
		ArrayList<Integer> hexTranslation = CoordConvert.cart2HexTile(translation);
		offsets.put(galRef, hexTranslation);//add translation to list
		
		logger.log(Level.INFO, "Translated " + galRef + " by (u,v,w)= " + hexTranslation.get(0) + " " 
				+ hexTranslation.get(1) + " " + hexTranslation.get(2));
		
		int xStep = 0; //randomly selected displacements
		int yStep = 0;
		double theta = 0.0; //direction to walk in
		double stepSize = 10;
		double adjustment = 1.1;
		
		ArrayList<Polygon> placedPolys = new ArrayList<Polygon>(); //holds polys to reference for new placements
		ArrayList<String> placedPolyNames = new ArrayList<String>();
		placedPolys.add(polys.get(0));
		placedPolyNames.add(galRefs.get(0));
		
		//Polygon referencePoly;
		//String referenceGalRef;
		
		Polygon selectedPoly;
		String selectedGalRef;
		boolean collisionFlag;
		for (int i = 1; i < polys.size(); i++) {
			collisionFlag = true;
			
			//select next polygon
			selectedPoly = polys.get(i);
			selectedGalRef = galRefs.get(i);
			logger.log(Level.INFO, "Selected " + selectedGalRef);
			
			Rectangle2D selectedBounds = selectedPoly.getBounds2D();
			
			//for (int i = 0; i < selectedBounds.)
			
			//Area selectedArea = new Area(selectedPoly);			
			
			//select random direction
			theta = rand.nextDouble()*2*Math.PI;
			logger.log(Level.INFO, "Theta " + theta);
			
			//Step out from reference poly in theta direction
			xStep = (int) (stepSize*Math.cos(theta)+0.5);
			yStep = (int) (stepSize*Math.sin(theta)+0.5);
			
			logger.log(Level.INFO, "xStep, yStep= " + xStep + " " + yStep);
			
			int cumuX = 0; //total displacement in this direction
			int cumuY = 0;
			
			while (collisionFlag) {
				collisionFlag = false; //assume no collision, then find one below
				
				//step
				selectedPoly.translate((int) (xStep), (int) (yStep));
				logger.log(Level.INFO, "Translated " + selectedGalRef + " by (x,y)= " + xStep + " " + yStep);
				
				cumuX += xStep;
				cumuY += yStep;				
				logger.log(Level.INFO, "cumuX, cumuY= " + cumuX + " " + cumuY);
				
				for (Polygon referencePoly: placedPolys) {
					Area intersect = new Area();
					intersect.add(new Area(selectedPoly));
					
					intersect.intersect(new Area(referencePoly));
					
					if (!intersect.isEmpty()) {
						collisionFlag = true;
						logger.log(Level.INFO, "COLLISION!");
					}							
				}
				
				if (!collisionFlag) {
					placedPolys.add(selectedPoly);
					placedPolyNames.add(selectedGalRef);
					logger.log(Level.INFO, "Poly placed: " + selectedGalRef);
					
				}
				else { //step further out, larger step size, continue
					xStep+=xStep;//*adjustment;
					yStep+=yStep;//*adjustment;
					logger.log(Level.INFO, "Poly not placed.  New step: " + xStep + " " + yStep);
				}
			}
			ArrayList<Double> cartTranslation = new ArrayList<Double>();
			cartTranslation.add((double) cumuX);
			cartTranslation.add((double) cumuY);
			
			hexTranslation = CoordConvert.cart2HexTile(cartTranslation);//new ArrayList<Integer>();
			
			logger.log(Level.INFO, "Translated " + selectedGalRef + " by (u,v,w)= " + hexTranslation.get(0) + " " 
					+ hexTranslation.get(1) + " " + hexTranslation.get(2));
			
			//Convert currTranslation to hexCoords before adding below!  Also, line 200.  Then, test this code separately.
			
			offsets.put(selectedGalRef, hexTranslation);
		}
		
		//plot polys as mesh lineloops
		
		ArrayList<ColorRGBA> colorList = new ArrayList<ColorRGBA>();
		colorList.add(ColorRGBA.Blue);
		colorList.add(ColorRGBA.Brown);
		colorList.add(ColorRGBA.Cyan);
		colorList.add(ColorRGBA.Magenta);
		colorList.add(ColorRGBA.Orange);
		colorList.add(ColorRGBA.Pink);
		colorList.add(ColorRGBA.Red);
		colorList.add(ColorRGBA.White);
		colorList.add(ColorRGBA.Yellow);
		colorList.add(ColorRGBA.Gray);		
		
		//ArrayList<Double> loc;
		float x1;
		float y1;
		CustomMesh mesh1;
		Geometry geo1;
		Material mat1;
		int j = 0;
		for (Polygon poly: placedPolys) {
			mesh1 = new CustomMesh(poly.xpoints.length);
			mesh1.setMode(Mode.LineLoop);
			
			if (j > colorList.size()-1) {
				j = 0;
			}
			mesh1.setColor(colorList.get(j));						
			mesh1.setPointSize(5);		
			
			for (int i = 0; i < poly.npoints; i++) { //ArrayList<Integer> location : locations) {
				
				x1 = poly.xpoints[i];
				y1 = poly.ypoints[i];
				
				mesh1.addVertex(x1, y1, 0);			
			}
			mesh1.finish();
			mesh1.setStatic();
			
			geo1 = new Geometry("points", mesh1);
			mat1 = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
			mat1.setBoolean("VertexColor", true);
			//mat.setColor("Color", ColorRGBA.Green);
			geo1.setMaterial(mat1);
			node.attachChild(geo1);
			j++;
		}
		
		
		
		
		
		
		
		
		
		
		return false;
	}

}
